#include "maps.h"
#include <QImage>
#include <math.h>

/* class multimap */
Multimap::Multimap() :
	_iMaxX(0), _iMaxY(0){ VERBOSE("constructing empty multimap");};
Multimap::Multimap(uint x, uint y) {
	MAXVERBOSE("constructing multimap");
	resize(x,y);
}
void Multimap::resize(uint x, uint y){
	_iMaxX = x;
	_iMaxY = y;
	_iMax = 0;
	_iMin = 255;
	std::ostringstream s;
	s << "x: "<<_iMaxX<<" y: "<<_iMaxY;
	VERBOSE("resizing multimap " +s.str());
	_aMap.resize(x);
	for (uint i = 0; i<x; ++i) {
		_aMap[i].resize(y);
		for (uint j = 0; j<y; ++j)
			_aMap[i][j] = 0;
	}
}
QImage Multimap::toGrayScale() const {
	VERBOSE("converting to grayscale");
	std::ostringstream s;
	QImage i(getWidth(),getHeight(),QImage::Format_RGB32);
	for (int x = 0; x<int(getWidth());++x)
	for (int y = 0; y<int(getHeight());++y) {
		i.setPixel(x,y,qRgb(getXY(x,y),getXY(x,y),getXY(x,y)));
	}
	return i;
}
QImage Multimap::toHeatmap() const {
	VERBOSE("converting to heatmap");
	std::ostringstream s;
	QImage i(getWidth(),getHeight(),QImage::Format_RGB32);
	for (int x = 0; x<int(getWidth());++x)
	for (int y = 0; y<int(getHeight());++y) {
/*		if (getXY(x,y)!=255 && getXY(x,y)>0) { //DEBUG
			s<<getXY(x,y);
			VERBOSE(s.str());
			s.str("");
		}*/
		i.setPixel(x,y,qRgb(getXY(x,y),0,255-getXY(x,y)));
	}
	return i;
}
bool	Multimap::modified() const { return _bModified;}
Multimap::~Multimap() {
	MAXVERBOSE("destructing multimap");
}
uint		Multimap::getXY(uint x, uint y) const {
	if (validXY(x,y))
		return _aMap[x][y];
	else {
		std::ostringstream s;
		s << "x: "<<x<<" y: "<<y<<"combination not valid against mx: "<<getMaxX()<<" my: "<<getMaxY();
		WARNING(s.str());
		return 0;
	}
}
bool	Multimap::valid(const uint s, const uint e, const uint v) const	{
	if (s<=v && v<e)
		return true;
	else {
		std::ostringstream st;
		st<< "value " << v <<" is not within " << s <<":"<<e;
		WARNING(st.str());
		return false;
	}
}
void	Multimap::stretch() {
	if ((_iMin == 0 && _iMax == 255) || _iMin>_iMax)
		return;
	float offset = _iMin,
				max = _iMax - offset;
	std::ostringstream s;
	for (uint x = 0; x<getWidth(); ++x)
	for (uint y = 0; y<getHeight(); ++y) {
		s<<"set "<<getXY(x,y)<<" to ";
		setXY(x,y,int(round(((getXY(x,y)-offset)/max)*255)));
		s<<getXY(x,y)<<" ";
		MAXVERBOSE(s.str());
		s.str("");
	}
}
bool	Multimap::validXY(const uint x, const uint y) const	{				return validX(x) && validY(y); }
bool	Multimap::validX(const uint x) const	{								return valid(0,getMaxX(),x); }
bool	Multimap::validY(const uint y) const	{								return valid(0,getMaxY(),y); }
uint	Multimap::getMaxX() const	{											return _iMaxX; }
uint	Multimap::getMaxY() const	{											return _iMaxY; }
uint	Multimap::getWidth() const {										return getMaxX();}
uint	Multimap::getHeight() const {										return getMaxY();}
void	Multimap::setXY(uint x, uint y, uint v) {
	if (v>_iMax)	_iMax = v;
	if (v<_iMin)	_iMin	= v;
	if (validXY(x,y)) {
		_aMap[x][y] = v;
		_bModified = true;
	}
}

/* class trimap */
Trimap::Trimap() {
	Multimap();
}
Trimap::Trimap(int x, int y) {
	Trimap((uint)x, (uint)y);
}
Trimap::Trimap(uint x, uint y) {
	Multimap();
	resize(x,y);
}
void Trimap::resize(uint x, uint y){
	Multimap::resize(x,y);
	_aMarkers[Trimap::object] = 0;
	_aMarkers[Trimap::background] = 0;
	_aMarkers[Trimap::unknown] = x*y;
}
void Trimap::setXY(uint x, uint y, obu v) {
	--_aMarkers[Multimap::getXY(x,y)];
	Multimap::setXY(x,y,v);
	++_aMarkers[v];
}
Trimap::obu Trimap::getXY(uint x, uint y) const{
	switch (Multimap::getXY(x,y)) {
		case 1: return object;
		case 2: return background;
		default: return unknown;
	}
}
uint Trimap::getCount(obu v) const{
	return _aMarkers[v];
}
bool	Trimap::markSquare(int o, int l, int u, int r) {
	if (validXY(l,o) && validXY(r,u)) {
		int ml = std::min(l,r),
				mr = std::max(l,r),
				mo = std::max(u,o),
				mu = std::min(u,o),
				x,y;
		for (x = 0; x<int(getWidth()); ++x)
		for (y = 0; y<int(getHeight()); ++y) {
			if (x<ml || x>mr || y<mu || y>mo)	setXY(x,y,background);
			else															setXY(x,y,object);
		}
		return true;
	} else {
		return false;
	}
}
bool Trimap::fromFile(std::string s) {
	QImage i;
	if (!(i.load(s.c_str()) && !i.isNull())) { ERROR("Can't read trimap from file "+s);return false; }
	else {
		if (getHeight()==0 && getWidth()==0)
			resize(i.size().width(),i.size().height());
		if (i.size().width()!=int(getWidth()) || i.size().height()!=int(getHeight())) { ERROR("Sizes of trimap and image do not match"); return false; }
		else {
			for (uint x = 0; x<getWidth(); ++x)
			for (uint y = 0; y<getHeight(); ++y) {
				switch (qRed(i.pixel(x,y))) {
					case 1: setXY(x,y,object); break;
					case 2: setXY(x,y,background); break;
					default: setXY(x,y,unknown); break;
				}
			}
			return true;
		}
	}
}
bool Trimap::toFile(std::string s) {
	QImage i = QImage(getWidth(), getHeight(),QImage::Format_RGB32);
	QRgb		o = qRgb(1,0,255),
					b = qRgb(2,255,0),
					u = qRgb(0,0,0);
	for (uint x = 0; x<getWidth(); ++x)
	for (uint y = 0; y<getHeight(); ++y) {
		std::ostringstream s;
		s.str("");
		s<<getXY(x,y);
		VERBOSE(s.str());
		switch (getXY(x,y)) {
			case object: i.setPixel(x,y,o); break;
			case background: i.setPixel(x,y,b); break;
			default: i.setPixel(x,y,u); break;
		}
	}
	return i.save(QString(s.c_str()));
}
